﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Windows.Threading;

namespace Microsoft.Forums.ForumsClient.ThreadedView
{
    /// <summary>
    /// A custom ObservableCollection which allows edits and additions to be performed from
    /// background threads.  This class which automatically marshall these operations back to the ui thread
    /// </summary>
    /// <typeparam name="T">Type contained in collection</typeparam>
    public class DispatchingObservableCollection<T> : ObservableCollection<T>
    {
        private Dispatcher dispatcherUI;

        /// <summary>
        /// Constructor for DispatchingObservableCollection.  Takes a dispatcher for the ui thread.
        /// </summary>
        /// <param name="dispatcher">Dispatcher for the UI thread</param>
        public DispatchingObservableCollection(Dispatcher dispatcher)
        {
            if (dispatcher == null) throw new ArgumentNullException("dispatcher", "dispatcher must not be null");
            dispatcherUI = dispatcher;
        }

        /// <summary>
        /// Overrides default SetItem functionality 
        /// by marshalling calls back to the dispatcher thread
        /// </summary>
        /// <param name="index">Index of item</param>
        /// <param name="item">item to set</param>
        protected override void SetItem(int index, T item)
        {
            if (dispatcherUI.CheckAccess())
            {
                base.SetItem(index, item);
            }
            else
            {
                dispatcherUI.Invoke(
                    DispatcherPriority.Send,
                    (Action)delegate
                        {
                            SetItem(index, item);
                        });
            }
        }

        /// <summary>
        /// Overrides default RemoveItem functionality 
        /// by marshalling calls back to the dispatcher thread
        /// </summary>
        /// <param name="index">index of item to remove</param>
        protected override void RemoveItem(int index)
        {
            if (dispatcherUI.CheckAccess())
            {
                base.RemoveItem(index);
            }
            else
            {
                dispatcherUI.Invoke(
                    DispatcherPriority.Send,
                    (Action)delegate
                    {
                        RemoveItem(index);
                    });
            }
        }

        /// <summary>
        /// Overrides default ClearItems functionality 
        /// by marshalling calls back to the dispatcher thread
        /// </summary>
        protected override void ClearItems()
        {
            if (dispatcherUI.CheckAccess())
            {
                base.ClearItems();
            }
            else
            {
                dispatcherUI.Invoke(
                    DispatcherPriority.Send,
                    (Action)delegate
                    {
                        ClearItems();
                    });
            }
        }

        /// <summary>
        /// Overrides default InsertItem functionality 
        /// by marshalling calls back to the dispatcher thread
        /// </summary>
        /// <param name="index">index to insert</param>
        /// <param name="item">item to insert</param>
        protected override void InsertItem(int index, T item)
        {
            if (dispatcherUI.CheckAccess())
            {
                base.InsertItem(index, item);
            }
            else
            {
                dispatcherUI.Invoke(
                    DispatcherPriority.Send,
                    (Action)delegate
                    {
                        InsertItem(index, item);
                    });
            }
        }

        /// <summary>
        /// Overrides default MoveItem functionality 
        /// by marshalling calls back to the dispatcher thread
        /// </summary>
        /// <param name="oldIndex">index of item to move</param>
        /// <param name="newIndex">new index to mvoe item to</param>
        protected override void MoveItem(int oldIndex, int newIndex)
        {
            if (dispatcherUI.CheckAccess())
            {
                base.MoveItem(oldIndex, newIndex);
            }
            else
            {
                dispatcherUI.Invoke(
                    DispatcherPriority.Send,
                    (Action)delegate
                    {
                        MoveItem(oldIndex, newIndex);
                    });
            }
        }
    }
}
